#include "defines.h"
#include "softvec.h"
#include "kernel.h"
#include "kozos.h"
#include "lib.h"

static uint32 systime = 0;

uint32 get_systime()
{
    return systime;
}

static kz_thread *alarm_list[THREAD_NUM];
static int alarm_size = 0;

void set_alarm(kz_thread *thp, uint32 tick)
{
    thp->flags |= THREAD_WAITALARM;
    thp->alarm.count = tick;
    thp->alarm.index = alarm_size;
    alarm_list[alarm_size] = thp;
    alarm_size++;
}

void cancel_alarm(kz_thread *thp) // THREAD_WAITALARMが立っていなくても呼べる
{
    if (thp->flags & THREAD_WAITALARM) {
        int i;

        thp->flags &= ~THREAD_WAITALARM;
        alarm_size--;

        if ((i = thp->alarm.index) != alarm_size) {
            kz_thread *p = alarm_list[alarm_size];
            alarm_list[i] = p;
            p->alarm.index = i;
        }
    }
}

// 待ちスレッドそれぞれのカウント減らすので、alarm_sizeに比例した時間がかかる(少なければ速い)
static void alarm_task()
{
    int i;

    for (i = 0; i < alarm_size; ) {
        kz_thread *p = alarm_list[i];

        ASSERT(p->flags & THREAD_WAITALARM, "alarm_list error");

        if (p->alarm.count) {
            p->alarm.count--;
            i++;
        } else {
            cancel_alarm(p); // alarm_sizeが1減る
            thread_wakeup(p);
        }
    }
}

#define TCR8_2   ((volatile uint8 *) 0xffff90)
#define TCR8_3   ((volatile uint8 *) 0xffff91)
#define TCORA    ((volatile uint16 *) 0xffff94)
#define TCNT8    ((volatile uint16 *) 0xffff98)
#define TCSR8_2  ((volatile uint8 *) 0xffff92)
#define TCSR8_3  ((volatile uint8 *) 0xffff93)

static void timer_intr()
{
    *TCSR8_3 &= ~0x40;
    systime++;
    alarm_task();
}

void timer_init()
{
#if TICK_PER_SEC == 5
#define _TCORA 0xf9f9
#define _TCR82 0x0a
#elif TICK_PER_SEC == 10
#define _TCORA 0x7cf9
#define _TCR82 0x0a
#elif TICK_PER_SEC == 50
#define _TCORA 0x18f9
#define _TCR82 0x0a
#elif TICK_PER_SEC == 100
#define _TCORA 0x187c
#define _TCR82 0x0a
#elif TICK_PER_SEC == 500
#define _TCORA 0x1818
#define _TCR82 0x0a
#elif TICK_PER_SEC == 1000
#define _TCORA 0x3131
#define _TCR82 0x09
#elif TICK_PER_SEC == 5000
#define _TCORA 0x0931
#define _TCR82 0x09
#elif TICK_PER_SEC == 10000
#define _TCORA 0x0431
#define _TCR82 0x09
#else
#error TICK_PER_SEC is Bad.
#endif
    sys_setintr(SOFTVEC_TYPE_T8INTR_23, timer_intr);
    *TCR8_2 = 0x00;
    *TCR8_3 = 0x4c;
    *TCORA  = _TCORA;
    *TCNT8  = 0;
    *TCSR8_3 &= 0x00;
    *TCR8_2 = _TCR82;
}
